#pragma once
#include "nanovg.h"
#include <chrono>
#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <stdexcept>
#include <string>
#include <thread>
#include <ranges>

#include "reflect.hpp"

namespace mb_shell {
std::string wstring_to_utf8(std::wstring const &str);
std::wstring utf8_to_wstring(std::string const &str);
bool is_win11_or_later();
bool is_light_mode();
bool is_acrylic_available();
std::optional<std::string> env(const std::string &name);
bool is_memory_readable(const void *ptr);
NVGcolor parse_color(const std::string &str);
std::string format_color(NVGcolor color);
void set_thread_locale_utf8();
void set_thread_name(const std::string &name);

std::vector<std::string> split_string(const std::string &str, char delimiter);

struct task_queue {
public:
    task_queue();

    ~task_queue();

    template <typename F, typename... Args>
    auto add_task(F &&f, Args &&...args)
        -> std::future<std::invoke_result_t<F, Args...>> {
        using return_type = std::invoke_result_t<F, Args...>;

        if (stop) {
            throw std::runtime_error("add_task called on stopped task_queue");
        }

        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...));

        std::future<return_type> res = task->get_future();

        {
            std::lock_guard<std::mutex> lock(queue_mutex);
            tasks.emplace([task]() { (*task)(); });
        }

        condition.notify_one();
        return res;
    }

private:
    void run();

    std::thread worker;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};

struct perf_counter {
    std::chrono::high_resolution_clock::time_point start;
    std::chrono::high_resolution_clock::time_point last_end;
    std::string name;
    void end(std::optional<std::string> block_name = {});
    perf_counter(std::string name);
};

template <typename E> constexpr std::string_view string_from_enum(E e) {
    return reflect::enum_name(e) |
           std::views::transform([](char c) { return c == '_' ? '-' : c; }) |
           std::ranges::to<std::string>();
}

template <typename E> constexpr auto create_enum_map() {
    constexpr auto min = reflect::enum_min(E{});
    constexpr auto max = reflect::enum_max(E{});
    constexpr size_t count = max - min + 1;

    std::array<std::pair<std::string_view, E>, count> map{};

    size_t index = 0;
    for (int i = min; i <= max; ++i) {
        E value = static_cast<E>(i);
        auto name = reflect::enum_name(value);
        if (!name.empty() && name != "") {
            map[index++] = {name, value};
        }
    }

    return map;
}

template <typename E>
constexpr std::optional<E> enum_from_string(std::string_view str) {
    static constexpr auto enum_map = create_enum_map<E>();

    for (const auto &[name, value] : enum_map) {
        // accept both '-' and '_' as word separators
        if ((str |
             std::views::transform([](char c) { return c == '-' ? '_' : c; }) |
             std::ranges::to<std::string>()) == name) {
            return value;
        }
    }
    return std::nullopt;
}

} // namespace mb_shell